import dayjs from 'dayjs';
import minMax from 'dayjs/plugin/minMax';
import { toNumber } from 'lodash-es';

import type { Task, Scale, TaskRecord, Progress } from '../types/progress';
import { TASK_STATUS } from '../types/progress';

dayjs.extend(minMax);

function dispenseTaskToScales(taskId: string, task: Task, scales: Scale[]): Scale[] {
  if (!taskId || typeof taskId !== 'string') {
    throw new Error('Invalid taskId');
  }
  if (!task || typeof task !== 'object') {
    throw new Error('Invalid task');
  }
  if (!scales || !Array.isArray(scales)) {
    throw new Error('Invalid scales');
  }

  scales.forEach((scale) => {

    if (task.endDate && task.endDate.isBefore(scale.date)) {
      scale.planTaksIds.done.push(taskId);
    } else if (task.startDate && scale.date.isBefore(task.startDate)) {
      scale.planTaksIds.todo.push(taskId);
    } else if (task.startDate && task.endDate) {
      scale.planTaksIds.doing.push(taskId);
    }

    const taskStatus = getTaskStatus(scale.date, task)
    if (taskStatus.includes(TASK_STATUS.START_ON_TIME)) {
      scale.realTaskIds.startOnTime.push(taskId);
    }
    if (taskStatus.includes(TASK_STATUS.START_LAG)) {
      scale.realTaskIds.startLag.push(taskId);
    }
    if (taskStatus.includes(TASK_STATUS.DOING_AHEAD)) {
      scale.realTaskIds.doingAhead.push(taskId);
    }
    if (taskStatus.includes(TASK_STATUS.DOING_LAG)) {
      scale.realTaskIds.doingLag.push(taskId);
    }
    if (taskStatus.includes(TASK_STATUS.DOING_NOT_START)) {
      scale.realTaskIds.doingNotStart.push(taskId);
    }
    if (taskStatus.includes(TASK_STATUS.DONE)) {
      scale.realTaskIds.done.push(taskId);
    }
    if (taskStatus.includes(TASK_STATUS.DONE_LAG)) {
      scale.realTaskIds.doneLag.push(taskId);
    }
    if (taskStatus.includes(TASK_STATUS.NULL)) {
      scale.realTaskIds.null.push(taskId);
    }
  });

  return scales;
}

export function getComponentsIds(taskIds: string[], tasks: TaskRecord): string[] {
  let componentsIds: string[] = [];
  taskIds.forEach((id) => {
    const singleTaskComponentIds = tasks[id].componentIds;
    if (singleTaskComponentIds) {
      componentsIds.push(...singleTaskComponentIds);
    }
  });
  return componentsIds;
}

/**
 * @abstract: 整理传入的进度数据
 */
export function progressToTasksAndScales(progress: Progress[]) {
  // 用于算出整个项目的最早开始时间和最晚结束时间
  const startSeq: dayjs.Dayjs[] = [];
  const endSeq: dayjs.Dayjs[] = [];

  const tasks: TaskRecord = {};
  let scales: Scale[] = [];

  progress.forEach((item) => {
    // 只需要处理与当前模型有关的进度数据
    // 以下三个条件有一个不成立，都说明该进度任务与当前模型无关
    if (item.uid && item.taskId && item.compentsIds) {
      let compentsIds: any;

      // 整理从后端返回的构件id列表
      compentsIds = item.compentsIds.replaceAll('[', ''); // 清除无用的字符 '['
      compentsIds = compentsIds.replaceAll(']', ''); // 清除无用的字符']'
      compentsIds = compentsIds.split(','); // 按逗号','将构件id列表字符串拆分成构件id列表数组

      // 计划完成率=(填报日期-计划开始日期)/(计划结束日期-计划开始日期)
      const planStartDate = dayjs(item.taskStarttime);
      const planEndDate = dayjs(item.taskEndtime);
      const duration = planEndDate.diff(planStartDate, 'day');
      const fillInfoDate = item.fillInfoDate ? dayjs(item.fillInfoDate) : null;
      let planFinishRate: number = 0;
      if (fillInfoDate && fillInfoDate.isAfter(planStartDate)) {
        planFinishRate = fillInfoDate.diff(planStartDate, 'day') / duration;
      }

      // 实际完成率=完成率/工程量
      const engineeringAmount = toNumber(item.engineeringAmount);
      const completionAmount = toNumber(item.completionAmount);
      const realFinishRate = completionAmount / engineeringAmount;

      startSeq.push(planStartDate);
      endSeq.push(planEndDate);

      // progress数据转换成Record<string, Task>
      tasks[item.uid] = {
        name: item.taskName,
        parentTask: item.parentTask,
        weight: toNumber(item.weight),
        startDate: planStartDate,
        endDate: planEndDate,
        duration: duration,
        realStartDate: item.realStarttime ? dayjs(item.realStarttime) : null,
        realEndDate: item.realEndtime ? dayjs(item.realEndtime) : null,
        planCode: item.taskId ?? null,
        componentIds: compentsIds,
        engineeringAmount: engineeringAmount,
        completionAmount: completionAmount,
        unit: item.unit,
        planFinishRate: Math.min(1, planFinishRate), // 保证值的范围处于0-1之间
        realFinishRate: Math.max(0, Math.min(1, realFinishRate)), // 保证值的范围处于0-1之间
        deviationAnalyse: item.deviationAnalyse,
        fillInfoDate: fillInfoDate,
      };
    }
  });

  // 计算出所有任务最早的开始日期和最晚的结束日期
  if (startSeq.length > 0 && endSeq.length > 0) {
    const scalesStart = dayjs.min(startSeq);
    const scalesEnd = dayjs.max(endSeq);

    // 初始化scales，填上日期date和星期week
    let scalesPointer = scalesStart;
    const scalesTermination = scalesEnd.add(1, 'day');
    while (scalesPointer.isBefore(scalesTermination)) {
      scales.push({
        date: scalesPointer,
        week: scalesPointer.format('ddd'),
        planTaksIds: {
          todo: [],
          doing: [],
          done: []
        },
        realTaskIds: {
          startOnTime: [],
          startLag: [],
          doingAhead: [],
          doingLag: [],
          doingNotStart: [],
          done: [],
          doneLag: [],
          null: [],
        }
      });
      scalesPointer = scalesPointer.add(1, 'day');
    }

    // 将所有任务tasks整理入scales，便于实现进度模拟动画
    Object.keys(tasks).forEach((key) => {
      scales = dispenseTaskToScales(key, tasks[key], scales);
    });
  } 

  return {
    tasks,
    scales,
  };
}

/**
 * @abstract 计算任务的状态
 */
export function getTaskStatus(today: dayjs.Dayjs, task: Task): TASK_STATUS[] {
  const res: TASK_STATUS[] = [];

  if (!today || !task) {
    throw new Error('Invalid input');
  }

  if (!task.realStartDate) {
    if (today.isBefore(task.startDate) || today.isSame(task.startDate)) {
      res.push(TASK_STATUS.START_ON_TIME);
    } else if (today.isAfter(task.startDate)) {
      res.push(TASK_STATUS.START_LAG);
    }
  }
  
  if (task.realFinishRate && task.realFinishRate > 0 && task.realFinishRate < 1) {
    if (task.realFinishRate >= task.planFinishRate) {
      res.push(TASK_STATUS.DOING_AHEAD);
    } else {
      res.push(TASK_STATUS.DOING_LAG);
    }
  } else if (today.isAfter(task.startDate) && !task.realStartDate) {
    res.push(TASK_STATUS.DOING_NOT_START);
  }

  if (today.isAfter(task.endDate) && !task.realEndDate) {
    res.push(TASK_STATUS.DONE_LAG);
  } else if (task.realEndDate) {
    res.push(TASK_STATUS.DONE);
  }

  return res;
}
